home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / DynaDoodle / DoodleBundle.m < prev    next >
Text File  |  1993-12-15  |  7KB  |  132 lines

  1. /**------------------------------------*------------------------------------**
  2.     RELEASED TO THE PUBLIC DOMAIN BY CODEWORKS DEVELOPMENT, DECEMBER 1993.
  3.     You may freely copy, distribute and reuse the code in this example. Codeworks disclaims any warranty of any kind, expressed or implied, as to its fitness for any particular use.
  4.     Author:  Andrew Vyrros, av@codeworks.com
  5.  **------------------------------------*------------------------------------**/
  6. /**------------------------------------*------------------------------------**
  7.     File:    DoodleBundle.m
  8.     Project: DynaDoodle
  9.     Info:    Implementation of the DoodleBundle class. DoodleBundle is a subclass of NXBundle designed specifically for managing dynamically loaded modules containing a single Doodle object. DoodleManager creates one DoodleBundle instance for each doodle module that it finds in the Library directories. When asked for its Doodle, a DoodleBundle will dynamically load its code file, create an instance of its Doodle subclass, and then return the instance to DoodleManager. DoodleBundle also provides localized strings describing its Doodle.
  10.  **------------------------------------*------------------------------------**/
  11.  
  12. #import "DoodleBundle.h"
  13. #import "Doodle.h"
  14. #import <appkit/appkit.h>
  15.  
  16.  
  17. @implementation DoodleBundle
  18.  
  19.  
  20. /**------------------------------------*------------------------------------**
  21.     Method:  initForDirectory:
  22.     Info:    Designated initializer, initializes new DoodleBundle instance. Doesn't do much other than connect to superclass. Just here for clarity.
  23.     Return:  (id)self
  24.  **------------------------------------*------------------------------------**/
  25. - initForDirectory:(const char *)path
  26. {
  27.   /* Connect to superclass. */
  28.   [super initForDirectory:path];
  29.  
  30.   /* Clear out instance variables. Paranoia code. */
  31.   doodleName = NULL;
  32.   doodleDescription = NULL;
  33.   doodle = nil;
  34.  
  35.   return self;
  36. }
  37.  
  38.  
  39. /**------------------------------------*------------------------------------**
  40.     Method:  free
  41.     Info:    Frees all resources allocated by DoodleBundle.
  42.     Return:  (id)nil
  43.  **------------------------------------*------------------------------------**/
  44. - free
  45. {
  46.   doodle = [doodle free];
  47.   return [super free];
  48. }
  49.  
  50.  
  51. /**------------------------------------*------------------------------------**
  52.     Method:  doodleName
  53.     Info:    Provides access to the doodleName instance variable. This string names the Doodle for the user. DoodleManager uses the string to create a PopUpList of available Doodle modules. DoodleBundle does not look up doodleName until needed.
  54.     Note that we have DoodleBundle implement doodleName, rather than Doodle itself. This allows us to get the name of a module before we have actually loaded the module or instantiated the Doodle. By doing this, we can fill a UI control with the names of the options while still deferring loading and instantiating until the modules are needed.
  55.     Instead of using the local string functions, we could have just taked the doodle module's file name and stripped the extension. However, that way user interface strings would be tied to file names. Also, our method lets us substitute more descriptive names--for instance, Face can become Smiley-Face.
  56.     Return:  (const char *)doodleName
  57.  **------------------------------------*------------------------------------**/
  58. - (const char *)doodleName
  59. {
  60.   /* If doodleName is not set, look up. */
  61.   if (!doodleName)
  62.     /*
  63.      * Note that there is some subtle trickery at work here. This call to
  64.      * NXLocalizedStringFromTableInBundle() uses the key "Name" to look up
  65.      * the value for the Doodle's name in the table Doodle.strings. This
  66.      * relies on the fact each Doodle is in a separate DoodleBundle, with its
  67.      * own Doodle.strings file. The file should have an entry under the key
  68.      * "Name" with the proper full string. This isn't the usual way to use
  69.      * NXLocalizedStringFromTableInBundle, but it works well here. 
  70.      */
  71.     doodleName = NXLocalizedStringFromTableInBundle("Doodle.strings", self, "Name", "Doodle", "Name of Doodle");
  72.  
  73.   return doodleName;
  74. }
  75.  
  76.  
  77. /**------------------------------------*------------------------------------**
  78.     Method:  doodleDescription
  79.     Info:    Provides access to the doodleDescription instance variable. This string describes the Doodle for the user. DoodleManager uses the string to fill a TextField. DoodleBundle does not look up doodleDescription until needed.
  80.     The same points about doodleName apply to doodleDescription.
  81.     Return:  (const char *)doodleDescription
  82.  **------------------------------------*------------------------------------**/
  83. - (const char *)doodleDescription
  84. {
  85.   if (!doodleDescription)
  86.     doodleDescription = NXLocalizedStringFromTableInBundle("Doodle.strings", self, "Description", "Generic Doodle.", "Description of Doodle");
  87.  
  88.   return doodleDescription;
  89. }
  90.  
  91.  
  92. /**------------------------------------*------------------------------------**
  93.     Method:  doodle
  94.     Info:    Provides access to the doodle instance variable. Each DoodleBundle has its own Doodle instance that is created from a dynamically loaded Doodle subclass. DoodleManager lets DoodleBundle keep track of the actual Doodle. DoodleBundle will not load its code file or create its Doodle instance until it is asked. This way, DoodleManager can create the DoodleBundles when it scans the Library directories, but not load the code files or create Doodles until they are needed.
  95.     Return:  (Doodle *)doodle
  96.  **------------------------------------*------------------------------------**/
  97. - (Doodle *)doodle
  98. {
  99.   Class               doodleClass;
  100.  
  101.   /* If doodle has not been created yet, */
  102.   if (!doodle)
  103.   {
  104.     /* Load principal class, create instance. */
  105.     doodleClass = [self principalClass];
  106.     doodle = [[doodleClass allocFromZone:[self zone]] init];
  107.  
  108.     /*
  109.      * Make sure that loaded doodle class is subclass of Doodle. Ideally, we
  110.      * would like to do this before creating the instance, but Object does
  111.      * not provide a method to check if a class is a subclass of another
  112.      * class. (Of course, you could write your own method in a Category of
  113.      * Object without too much trouble.) Instead, we check that the instance
  114.      * is a kind of Doodle, and free it if not. 
  115.      *
  116.      * Using Protocols would be the other option here. That would free us to
  117.      * created loaded classes that do not inherit from Doodle, as long as
  118.      * they implement the required methods. In the scope of this example, it
  119.      * is simpler to just use an abstract superclass. That also lets us have
  120.      * Doodle implement common behavior to be inherited by the loaded
  121.      * subclasses. 
  122.      */
  123.     if (![doodle isKindOf:[Doodle class]])
  124.       doodle = [doodle free];
  125.   }
  126.  
  127.   return doodle;
  128. }
  129.  
  130.  
  131. @end
  132.